package com.cmsz.cashier.controller;


import java.io.IOException;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.ClientProtocolException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.cmsz.cashier.bean.CashierOrderInoBean;
import com.cmsz.cashier.mapper.CashierOrderInfoBeanMapper;
import com.cmsz.cashier.utils.ParamUtil;
import com.cmsz.order.remote.BetterRemoteService;
import com.cmsz.order.remote.SignResult;
import com.cmsz.order.remote.WxRemoteService;
import com.cmsz.framework.ftl.FtlTemplateEngine;
import com.cmsz.framework.http.HttpClient;
import com.cmsz.framework.log.LogHandler;
import com.cmsz.framework.util.JsonUtil;
import com.cmsz.framework.util.KeyValueUtil;
import com.cmsz.framework.util.XmlUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 接收用户选择支付方式后请求，提交请求到统一支付后台
 * 统一支付后台响应后，组装自动提交报文到第三方支付结构
 * @author Eshin
 *
 */ 

@Controller
@RequestMapping("/cashier")
public class CashierCommitController {
	
	private static LogHandler logger = LogHandler.getLogger(CashierCommitController.class);
	@Autowired
	private HttpClient httpClient;
	@Autowired
	private FtlTemplateEngine messageTemplateEngine;
	@Resource(name="BankSecurityRemoting")
	private BetterRemoteService betterRemoteService;
	
	@Resource(name="wxRemoteService")
	private WxRemoteService wxRemoteService;
	@Autowired
	CashierOrderInfoBeanMapper cashierOrderInfoBeanMapper;
	@Value("${orderUrl}")
	private String orderUrl;
	@Value("${notifyUrl}")
	private String notifyUrl;
	@Value("${wxPayUrl}")
	private String wxPayUrl;
	@Value("${aliPayUrl}")
	private String aliPayUrl;
	@Value("${unionPayUrl}")
	private String unionPayUrl;
	@Value("${cashier.ali.returnurl:}")
	private String aliReturnUrl;
	@Value("${cashier.union.returnurl:}")
	private String unionReturnUrl;
	@Value("${cashier.wx.returnurl:}")
	private String wxReturnUrl;
	@ResponseBody
	@RequestMapping(value = "/submit",produces = "text/html; charset=utf-8", method = { RequestMethod.POST,
			RequestMethod.GET })
	public String submit(ModelMap map, CashierOrderInoBean orderBean) throws IOException {
//		1、接收支付表单提交
		try {
			logger.info("收银台|CashierOrderController|接收到用户选择支付方式后请求",orderBean);
			logger.messageReceive("收银台|CashierCommitController|接收到用户选择支付方式后请求："+orderBean.toString());
			
//			2、校验选择表单前数据的签名
			checkSign(orderBean);
			logger.info("收银台|CashierOrderController|校验选择表单前数据的签名完成",orderBean);
			
//		    3、订单入库
			cashierOrderInfoBeanMapper.insert(orderBean);
			logger.info("收银台|CashierOrderController|订单入库完成",orderBean);
			
//			根据支付方式设置必要信息
			setInfoByPayType(orderBean);
			
//		    4、组装下单请求报文
			//商户(仿真)发给收银台需要填U104_001，收银台发给商户前置需要为U101_001
			String activityCode =orderBean.getActivityCode();
			activityCode = ("U104_001".equals(activityCode))?"U101_001":activityCode;
			orderBean.setActivityCode(activityCode);
			String orderXml = messageTemplateEngine.genMessage("cashier_order.xml", orderBean);
			orderXml = orderXml.replaceAll("\t|\r|\n","");
			
			logger.info("收银台|CashierCommitController|组装下单请求报文完成",orderBean);
			
//		    5、报文签名
			orderXml = signForFront(orderXml);
			logger.info("收银台|CashierCommitController|请求报文签名完成",orderBean);
			
//		    6、发送请求报文
			logger.messageSend("收银台|CashierCommitController|向统一支付提交订单请求：" + orderXml);
			String frontRsp = sendToFront(orderXml);
			logger.messageReceive("收银台|CashierCommitController|接收到统一支付下单响应："+frontRsp);
			logger.info("收银台|CashierCommitController|发送请求报文到统一支付完成",orderBean);
			
//		    7、响应验签
			checkSignForFront(frontRsp);
			logger.info("收银台|CashierCommitController|响应报文验签完成",orderBean);
			
//		    8、响应校验
			checkCode(frontRsp);
			logger.info("收银台|CashierCommitController|响应报文校验完成",orderBean);
			
//		    9、提取订单参数组装第三方支付机构跳转页面
			if (orderBean.getPaymentType().contains("WEIXIN-NATIVE")) {
				String codeUrl = getCodeUrl(frontRsp);
				map.addAttribute("codeurl", codeUrl);
				map.addAttribute("orderBean", orderBean);
				logger.info("收银台|CashierCommitController|响应微信支付二维码 URL完成", orderBean);
				logger.messageSend("收银台|CashierCommitController|响应微信支付二维码 URL：" + codeUrl);
				return "page/wxcode";
			}else if(orderBean.getPaymentType().contains("WEIXIN-JSAPI")){
				String parameters = genParam(frontRsp);
				map.addAttribute("parameters", parameters);
				logger.info("收银台|CashierCommitController|响应微信公众号支付完成", orderBean);
				logger.messageSend("收银台|CashierCommitController|响应微信公众号支付：" + parameters);
				return "page/wxjsapi";
			}else{
				String paymentType = XmlUtils.parseNodeValueFromXml("<PaymentType>", "</PaymentType>", frontRsp);
				map.addAttribute("paymentType", paymentType);
				String parameters = XmlUtils.parseNodeValueFromXml("<Parameters><![CDATA[", "]]></Parameters>", frontRsp);
				map.addAttribute("parameters", parameters);
				logger.info("收银台|CashierCommitController|向第三方支付机构提交表单", orderBean);
				logger.messageSend("收银台|CashierCommitController|向第三方支付机构提交表单");
				// todo page/createForm.html
				return "page/createForm";
			}
		} catch (Exception e) {
			logger.error("收银台|CashierCommitController|订单提交失败,向用户返回异常界面"+e,e);
			
			if("013A17".equals(e.getMessage())|| e instanceof DuplicateKeyException){
				map.addAttribute("errMsg", "请勿重复提交订单");
			}
			map.addAttribute("orderBean", orderBean);
			return "page/error";
		}
	}

	/**
	 * 组装调起微信公众号支付所需参数
	 * json格式
	 * @param frontRsp
	 * @return
	 * @throws JsonProcessingException 
	 */
	private String genParam(String frontRsp) throws JsonProcessingException {
		String parameters = XmlUtils.parseNodeValueFromXml("<Parameters><![CDATA[", "]]></Parameters>", frontRsp);
//		改由后台实现
		Map<String, String>rspMap = KeyValueUtil.keyValueStringToMap(parameters);
/*      Map<String, String> wxParamMap = new HashMap<>();
		String  timeStamp =Long.toString( new Date().getTime());
		
		wxParamMap.put("appId", rspMap.get("appid"));
		wxParamMap.put("timeStamp",timeStamp.substring(0, timeStamp.length()-3)); //时间戳，自1970年以来的秒数
		wxParamMap.put("nonceStr", rspMap.get("nonce_str"));
		wxParamMap.put("package", "prepay_id="+rspMap.get("prepay_id"));
		wxParamMap.put("signType", "MD5");
		
		String mchId = rspMap.get("mch_id");
		String signStr = KeyValueUtil.mapToString(wxParamMap);
		String sign = wxRemoteService.signMd5(mchId, signStr);
		wxParamMap.put("paySign", sign);*/
		
		ObjectMapper mapper = new ObjectMapper();
		String paramJson = mapper.writeValueAsString(rspMap);
		return paramJson;
	}


	/**
	 * 获取微信预下单返回的支付二维码图片链接
	 * @param frontRsp
	 * @return
	 */
	private String getCodeUrl(String frontRsp) {
		String parameters = XmlUtils.parseNodeValueFromXml("<Parameters><![CDATA[", "]]></Parameters>", frontRsp);
		Map<String, String>rspMap = KeyValueUtil.keyValueStringToMap(parameters);
		String codeUrl = rspMap.get("code_url");
		return codeUrl;
	}

	/**
	 * 校验选择表单前数据的签名
	 * @param orderBean
	 */
	private void checkSign(CashierOrderInoBean orderBean) {
//		与页面显示的数据对应，不包含选择支付方式和Cerid，signvalue
		String orderValueStr = ParamUtil.genPageParamSignStr(orderBean);
		String cerId = orderBean.getCerID();
		String signature = orderBean.getSignValue();
		try {
			if (!betterRemoteService.verify(cerId, orderValueStr, signature)) {
				throw new RuntimeException("验签失败");
			}
		} catch (Exception e) {
			throw new RuntimeException("验签失败");
		}
	}
	/**
	 * 设置收银台returnURL
	 * 不管商户是否发送returnURL发送到第三方支付机构的returnURL都使用
	 * 收银台的。当第三方支付机构返回到收银台时，在通过数据库查找原returnURL回调
	 * 如果不配置收银台的returnURL则以商户发过来的为准
	 */
	private void setInfoByPayType(CashierOrderInoBean orderBean) {

		if(StringUtils.isNotBlank(aliReturnUrl) && orderBean.getPaymentType().contains("ALIPAY")){
			orderBean.setReturnUrl(aliReturnUrl);
		}
		if(StringUtils.isNotBlank(unionReturnUrl) && orderBean.getPaymentType().contains("UNIONPAY")){
			orderBean.setReturnUrl(unionReturnUrl);
		}
		if(StringUtils.isNotBlank(wxReturnUrl) && orderBean.getPaymentType().contains("WEIXIN")){
			orderBean.setReturnUrl(wxReturnUrl);
		}
	}

	/**
	 * 签名给统一支付，商户前置要验签
	 * @param orderXml
	 * @return
	 */
	private String signForFront(String orderXml) {
		StringBuilder plainText = new StringBuilder();
		String header = XmlUtils.parseNodeValueFromXml("<Header>", "</Header>", orderXml);
		String body = XmlUtils.parseNodeValueFromXml("<Body>", "</Body>", orderXml);
		plainText.append("<Header>").append(header).append("</Header>|<Body>").append(body).append("</Body>");
		try {
			String jsonStr = betterRemoteService.sign(null, plainText.toString());
			SignResult signResult = JsonUtil.jsonStr2Obj(jsonStr, SignResult.class);
			orderXml = XmlUtils.relaceNodeContent("<CerID>", "</CerID>", signResult.getCerId(), orderXml);
			orderXml = XmlUtils.relaceNodeContent("<SignValue>", "</SignValue>", signResult.getSignValue(), orderXml);
			return orderXml;
		} catch (Exception e) {
			throw new RuntimeException("签名失败："+e);
		}
	}
	
	private String sendToFront(String orderXml) throws ClientProtocolException, IOException {
		String merchantFrontRsp = httpClient.sendStringMsg(orderUrl, orderXml);
		if (StringUtils.isBlank(merchantFrontRsp)) {
			throw new RuntimeException("网络通讯异常，响应为空");
		}
		return merchantFrontRsp;
	}
	
	/**
	 * 对商户请求数据验签
	 * @param orderXml
	 */
	private void checkSignForFront(String orderXml) {
		StringBuilder plainText = new StringBuilder();
		String cerId = XmlUtils.parseNodeValueFromXml("<CerID>", "</CerID>", orderXml);
		String signature = XmlUtils.parseNodeValueFromXml("<SignValue>", "</SignValue>", orderXml);
		String header = XmlUtils.parseNodeValueFromXml("<Header>", "</Header>", orderXml);
		String body = XmlUtils.parseNodeValueFromXml("<Body>", "</Body>", orderXml);
		plainText.append("<Header>").append(header).append("</Header>|<Body>").append(body).append("</Body>");
		try {
			if (betterRemoteService.verify(cerId, plainText.toString(), signature)) {
				return;
			}
		} catch (Exception e) {
			throw new RuntimeException("验签失败："+e);
		}
	}
	
	/**
	 * 检查统一支付返回码
	 * @param xml
	 */
	private void checkCode(String xml) {
		
		String rspCode = XmlUtils.parseNodeValueFromXml("<RspCode>", "</RspCode>", xml);
		if (!"010A00".equals(rspCode)) {
			throw new RuntimeException("商户前置返回失败：" + rspCode);
		}
		String resultCode = XmlUtils.parseNodeValueFromXml("<ResultCode>", "</ResultCode>", xml);
		if (!"014A17".equals(resultCode)) {
			throw new RuntimeException("商户前置返回失败：" + resultCode);
		}
		if ("013A17".equals(resultCode)) {
			throw new RuntimeException(resultCode);
		}
	}
	
	/**
	 * 组装表单数据，客户端拿到表单自动提交给第三方支付机构
	 * @param xml
	 * @return
	 */
	private String createThirdForm(String xml) {
		String paymentType = XmlUtils.parseNodeValueFromXml("<PaymentType>", "</PaymentType>", xml);
		String parameters = XmlUtils.parseNodeValueFromXml("<Parameters><![CDATA[", "]]></Parameters>", xml);
		StringBuilder sb = new StringBuilder();
		String payUrl = wxPayUrl;
		if (paymentType.startsWith("ALIPAY")) {
			payUrl = aliPayUrl;
		} else if (paymentType.startsWith("UNIONPAY")) {
			payUrl = unionPayUrl;
		}
		sb.append("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" /></head><body><form id=\"paysubmit\" name=\"paysubmit\" action=\""+payUrl+"\" method=\"post\">");
		for(String s : parameters.split("&")) {
			int index = s.indexOf("=");
			String key = s.substring(0,index);
			String value = s.substring(index+1);
			sb.append("<input type=\"hidden\" name=\""+key+"\" id=\""+key+"\" value=\""+value+"\" />");
		}
		sb.append("</form><script>document.forms['paysubmit'].submit();</script></body></html>");
		return sb.toString();
	}

}
